package org.hard.aspect;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.hard.annotation.Repeat;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author: 王林博
 * @date: 2022年10月08日 16:55
 */
@Aspect
@Component
@Order(1)
@Slf4j
@AllArgsConstructor
public class RepeatAspect extends BaseAspect{

    @Autowired
    private RedissonClient redissonClient;

    /***
     * 定义controller切入点拦截规则，拦截Repeat注解的业务方法
     */
    @Pointcut("@annotation(repeat)")
    public void pointCut(Repeat repeat) {
    }

    /**
     * AOP分布式锁拦截
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    @Around("pointCut(repeat)")
    public Object repeatSubmit(ProceedingJoinPoint joinPoint, Repeat repeat) throws Throwable {
        String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());
        if (Objects.nonNull(repeat)) {
            // 获取参数
            Object[] args = joinPoint.getArgs();
            // 进行一些参数的处理，比如获取订单号，操作人id等
            String key = getValueBySpEL(repeat.lockKey(), parameterNames, args, "RepeatSubmit").get(0);
            RLock lock = redissonClient.getFairLock(key);
            boolean hasLock = false;
            try {
                // 锁等待时间，超时时间，时间单位
                hasLock = lock.tryLock(3, repeat.lockTime(), TimeUnit.SECONDS);
                if (hasLock) {
                    log.info("获取分布式锁成功，key={}", key);
                    return joinPoint.proceed();
                }else {
                    log.info("获取锁失败");
                    // 未获取到锁
                    throw new RuntimeException("请勿重复操作");
                }
            } catch (Throwable e) {
                log.error("切面分布式锁异常:{}", e);
                if(!Objects.equals("请勿重复操作",e.getMessage())){
                    lock.unlock();
                    throw new RuntimeException(e);
                }else {
                    throw new RuntimeException("请勿重复操作");
                }
            } finally {
                // 如果锁还存在，在方法执行完成后，释放锁
                if (repeat.unlock() && hasLock) {
                    lock.unlock();
                }
            }
        }
        return joinPoint.proceed();
    }
}
